package com.zs.toolbox.web2j.system.core.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zs.toolbox.web2j.auth.contants.WtSecurityConstant;
import com.zs.toolbox.web2j.auth.security.WtBCryptPasswordEncoder;
import com.zs.toolbox.web2j.common.exception.WtExceptions;
import com.zs.toolbox.web2j.common.web.WtCommonPropertiesSetter;
import com.zs.toolbox.web2j.system.message.domain.service.WtMessageClientService;
import com.zs.toolbox.web2j.system.core.entity.*;
import com.zs.toolbox.web2j.system.core.mapper.user.WtUserMapper;
import com.zs.toolbox.web2j.system.core.service.WtPermissionService;
import com.zs.toolbox.web2j.system.core.service.WtRoleService;
import com.zs.toolbox.web2j.system.core.service.WtUserRoleService;
import com.zs.toolbox.web2j.system.core.service.WtUserService;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户基本信息表 服务实现类
 * </p>
 *
 * @author eric.zhang
 * @since 2018-09-21
 */
@Service
public class WtUserServiceImpl extends ServiceImpl<WtUserMapper, WtUserEntity> implements WtUserService {
    @Autowired
    private WtBCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    WtMessageClientService wtMessageClientService;


    @Autowired
    WtRoleService wtRoleService;

    @Autowired
    WtUserRoleService wtUserRoleService;

    @Autowired
    WtPermissionService wtPermissionService;

    @Override
    public WtUserEntity selectByUsernameOrPhoneOrEmail(String name) {
        LambdaQueryWrapper<WtUserEntity> condition = new LambdaQueryWrapper<>();
        condition.and(i -> i.eq(WtUserEntity::getUsername, name).or()
                .eq(WtUserEntity::getPhone, name).or()
                .eq(WtUserEntity::getEmail, name));
        WtUserEntity wtUserEntity = baseMapper.selectOne(condition);
        return wtUserEntity;
    }

    @Override
    public String createrToken(WtUserEntity wtUserEntity) {
        Date expireTime = new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(6));

        List<String> auths = new ArrayList<>();

        List<WtRoleEntity> roleList = wtRoleService.selectUserRolesByUserId(wtUserEntity.getId());
        if (CollectionUtils.isNotEmpty(roleList)) {
            List<String> roleNames = roleList.stream()
                    .map(wtRoleEntity -> wtRoleEntity.getRoleName())
                    .collect(Collectors.toList());
            auths.addAll(roleNames);

            List<WtPermissionEntity> permissionList = wtPermissionService
                    .selectRolePermissionsByRoleIds(roleList.stream().map(WtRoleEntity::getId).collect(Collectors.toList()));
            List<String> getPermissionCodes = permissionList.stream().map(wtPermissionEntity -> wtPermissionEntity.getPermissionCode()).collect(Collectors.toList());
            auths.addAll(getPermissionCodes);
        }

        if(wtUserEntity.getUserType() == 2) {
            auths.add("ROLE_ADMIN");
        }

        String token = Jwts.builder()
                .setExpiration(expireTime)
                .signWith(SignatureAlgorithm.HS512, WtSecurityConstant.TokenSerect)
                .setSubject(wtUserEntity.getId() + "-" + StringUtils.join(auths, ","))
                .compact();
        return token;
    }

    @Override
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void saveNewUser(WtUserEntity saveEntity) {
        if (StringUtils.isNotBlank(saveEntity.getPhone())) {
            if (baseMapper.selectCount(new LambdaQueryWrapper<WtUserEntity>().eq(WtUserEntity::getPhone, saveEntity.getPhone())) > 0) {
                throw WtExceptions.E_USER_PHONE_USED;
            }
        }

        if (StringUtils.isNotBlank(saveEntity.getEmail())) {
            if (baseMapper.selectCount(new LambdaQueryWrapper<WtUserEntity>().eq(WtUserEntity::getEmail, saveEntity.getEmail())) > 0) {
                throw WtExceptions.E_USER_EMAIL_USED;
            }
        }

        if (StringUtils.isNotBlank(saveEntity.getUsername())) {
            if (baseMapper.selectCount(new LambdaQueryWrapper<WtUserEntity>().eq(WtUserEntity::getUsername, saveEntity.getUsername())) > 0) {
                throw WtExceptions.E_USER_USERNAME_USED;
            }
        }

        baseMapper.insert(saveEntity.setPassword(bCryptPasswordEncoder.encode(saveEntity.getPassword())));
    }

    @Override
    public WtUserEntity getEntityByPhoneOrEmail(String phoneOrEmail) {
        WtUserEntity wtUserEntity = baseMapper.
                selectOne(new LambdaQueryWrapper<WtUserEntity>().and(i ->
                        i.eq(WtUserEntity::getPhone, phoneOrEmail).or()
                                .eq(WtUserEntity::getEmail, phoneOrEmail)));
        return wtUserEntity;
    }

    @Override
    public void changePasswordByPhoneOrEmail(String phoneOrEmail, String newPassword) {
        WtUserEntity wtUserEntity = getEntityByPhoneOrEmail(phoneOrEmail);
        if (wtUserEntity == null) {
            throw WtExceptions.E_USER_NOEXIST;
        }
        WtUserEntity updateEntity = new WtUserEntity()
                .setPassword(bCryptPasswordEncoder.encode(newPassword))
                .setId(wtUserEntity.getId()).setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(updateEntity);
    }

    @Override
    public boolean matchPassword(String rawPassword, String encodePassword) {
        return bCryptPasswordEncoder.matches(rawPassword, encodePassword);
    }

    @Override
    public void bindPhone(Long userId, String phone) {
        WtUserEntity wtUserEntity = baseMapper.selectById(userId);
        if (StringUtils.isNotBlank(wtUserEntity.getPhone())) {
            throw WtExceptions.E_USER_PHONE_BINDED;
        }
        WtUserEntity entityByPhoneOrEmail = this.getEntityByPhoneOrEmail(phone);
        if (null != entityByPhoneOrEmail) {
            throw WtExceptions.E_USER_PHONE_USED;
        }

        WtUserEntity updateEntity = new WtUserEntity().setId(userId).setPhone(phone).setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(updateEntity);
    }

    @Override
    public void unbindPhone(Long userId, String phone) {
        WtUserEntity wtUserEntity = baseMapper.selectById(userId);
        if (!phone.equalsIgnoreCase(wtUserEntity.getPhone())) {
            throw WtExceptions.E_USER_PHONE_BELONG;
        }
        WtUserEntity updateEntity = new WtUserEntity().setId(userId).setPhone("").setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(updateEntity);
    }

    @Override
    public void bindeEmail(Long userId, String email) {
        WtUserEntity wtUserEntity = baseMapper.selectById(userId);
        if (StringUtils.isNotBlank(wtUserEntity.getEmail())) {
            throw WtExceptions.E_USER_EMAIL_BINDED;
        }
        WtUserEntity entityByPhoneOrEmail = this.getEntityByPhoneOrEmail(email);
        if (null != entityByPhoneOrEmail) {
            throw WtExceptions.E_USER_EMAIL_USED;
        }

        WtUserEntity updateEntity = new WtUserEntity().setId(userId).setEmail(email).setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(updateEntity);

    }

    @Override
    public void unbindEmail(Long userId, String email) {
        WtUserEntity wtUserEntity = baseMapper.selectById(userId);
        if (!email.equalsIgnoreCase(wtUserEntity.getEmail())) {
            throw WtExceptions.E_USER_EMAIL_BELONG;
        }

        WtUserEntity updateEntity = new WtUserEntity().setId(userId).setEmail("").setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(updateEntity);
    }

    @Override
    public WtUserWithRoleEntity getUserWithRoleById(Long id) {

        WtUserEntity wtUserEntity = getById(id);
        if (null != wtUserEntity) {
            List<Long> roleIds = wtUserRoleService.list
                    (new LambdaQueryWrapper<WtUserRoleEntity>().eq
                            (WtUserRoleEntity::getUserId, wtUserEntity.getId()))
                    .stream().map(WtUserRoleEntity::getRoleId).collect(Collectors.toList());


            Collection<WtRoleEntity> roleEntities =  roleIds.size() > 0 ? wtRoleService.listByIds(roleIds) : CollectionUtils.EMPTY_COLLECTION;

            WtUserWithRoleEntity wtUserWithRoleEntity =
                    new WtUserWithRoleEntity().setRoles(roleEntities);

            BeanUtils.copyProperties(wtUserEntity, wtUserWithRoleEntity);
            return wtUserWithRoleEntity;
        } else {
            return null;
        }
    }

    @Override
    @Transactional
    public void saveUserRole(Long userId, List<String> roleIds, Long createrUserId) {

        /*删除原有用户角色*/
        wtUserRoleService.removeById(userId);

        /*提取出真实存在的roleId*/
        Collection<WtRoleEntity> roleEntities = wtRoleService.listByIds(roleIds);

        /*添加新角色*/
        List<WtUserRoleEntity> permissionEntityList = roleEntities.stream().map(roleEntity -> {
            WtUserRoleEntity wtUserRoleEntity = new WtUserRoleEntity();
            WtCommonPropertiesSetter.setDefaultCommonProperty(wtUserRoleEntity, createrUserId);
            wtUserRoleEntity.setRoleId(roleEntity.getId()).setUserId(userId);
            return wtUserRoleEntity;
        }).collect(Collectors.toList());

        wtUserRoleService.saveBatch(permissionEntityList);
    }
}
